Angular 預設是一個單頁應用程式(SPA),為了實現不同頁面內容的切換,提供了強大的路由功能。透過路由,使用者可以根據網址(URL)的變化,動態顯示不同的元件,就像在多個頁面之間切換一樣,但實際上不需要重新載入整個網頁。這就是 client-side routing 的特點。
可以在 app.routes.ts
中透過建立 Routes
陣列來定義路由設定,每個路由物件包含以下主要的屬性
path
:路由的路徑,對應 URL 的一部分component
:當路由匹配時要顯示的元件export const routes: Routes = [
{
path: '',
component: HomeComponent
},
{
path: 'tasks',
component: TasksComponent
}
]
建立完路由後,需要在 main.ts
中使用 provideRouter
來註冊路由設定,這樣應用程式才能識別並處理路由。
bootstrapApplication(AppComponent, {
providers: [provideRouter(routes)]
})
最後需要在應用程式的根元件模板中加入 router-outlet
,來顯示根據路由設定所對應的元件內容。路由切換時,會在這個標籤內動態載入對應的元件。
需在元件中引入 RouterOutlet,才能在模板中使用
<router-outlet>
標籤。
import { RouterOutlet } from '@angular/router';
@Component({
...
imports: [RouterOutlet]
...
})
<router-outlet />
使用 router-link
指令來建立導航連結,點擊後會改變 URL 並觸發路由切換。
需要引入 RouterLink 指令,才能在模板中使用
routerLink
屬性。
import { RouterLink } from '@angular/router';
@Component({
...
imports: [RouterLink]
...
})
<a routerLink="/tasks">任務列表</a>
使用 ..
來導航到上一層父路由
queryParams
:用來設定查詢參數
<!-- task-detail.component.html -->
<!-- 實際網址會變成 /users?ref=dashboard -->
<a [routerLink]="['..']" [queryParams]="{ ref: 'dashboard' }">返回使用者列表</a>
routerLinkActive
指令,用來為當前路由匹配的連結添加 CSS 類別,通常用於標示當前頁面。
需要引入 RouterLinkActive 指令,才能在模板中使用
routerLinkActive
屬性。
import { RouterLinkActive } from '@angular/router';
@Component({
...
imports: [RouterLinkActive]
...
})
<a routerLink="/tasks" routerLinkActive="active">任務列表</a>
需要在元件中使用程式導航時,可使用 Router
來直接導航到指定的路由。
navigate
:Router
的方法,用來導航到指定的路由。export class SomeComponent {
private router = inject(Router);
goToTasks() {
this.router.navigate(['/tasks']);
}
}
若是提交表格後,想要禁止使用者使用瀏覽器的返回按鈕回到前一頁,可以使用 replaceUrl
選項來取代當前的歷史紀錄條目。
this.router.navigate(['/'], { replaceUrl: true });
可以在路由路徑中使用 :parameterName
來定義動態路由參數。
:
後面的參數名稱,可以自訂,但要與取得參數時使用的名稱一致。
{
path: 'tasks/:taskId', <domain>/tasks/1
component: TaskDetailComponent
}
在使用動態路由時,繫結連結需要傳入參數值時,可以使用字串拼接或陣列語法。
<a [routerLink]="'/tasks/' + task.id">{{ task.title }}</a>
<a [routerLink]="['/tasks', task.id]">{{ task.title }}</a>
而在元件中取得路由參數或查詢參數有兩種方式:
需要在 provideRouter 時使用
withComponentInputBinding
,來啟用 input 綁定路由參數的功能。
bootstrapApplication(AppComponent, {
providers: [provideRouter(routes,withComponentInputBinding())]
})
export class TaskDetailComponent {
taskId = input<string>(); // 取得路由參數 taskId
query = input<string | null>(); // 取得查詢參數 query
// 例如: /tasks/1?query=angular
}
ActivatedRoute
服務paramMap:用來取得路由參數的 Observable 物件,可觀察路由參數的變化。
export class TaskDetailComponent {
private activatedRoute = inject(ActivatedRoute);
this.activatedRoute.paramMap.subscribe({
next: (params) => {
// 取得路由參數 taskId
conole.log(params.get('taskId'));
// 取得查詢參數 query
console.log(params['query']);
}
});
}
可以在路由設定中定義子路由,來建立巢狀的路由結構。
{
path: 'user/:userId',
component: UserComponent,
children: [
{
path: 'tasks,
component: TaskDetailComponent
}
]
}
也需要在父元件的模板中加入 <router-outlet>
,來顯示子路由對應的元件。
<h2>任務列表</h2>
<router-outlet></router-outlet>
在巢狀路由中路徑是相對於父路由的,所以在內部中導航時,可不需要加上父元件的路徑。
<!-- 不需要加上父元件的路徑 -->
<a [routerLink]="[task]">{{ task.title }}</a>
若需要巢狀路由繼承父路由的參數的話,可以在 provideRouter 時使用 withRouterConfig
來設定 paramsInheritanceStrategy
路由引數的繼承策略,這樣才可以使用 input 的方式來取得父路由的參數。
emptyOnly
:只有當路由本身具有空路徑時,才會繼承父路由的引數。always
:無論路由本身是否具有空路徑,都會繼承父路由的引數。bootstrapApplication(AppComponent, {
providers: [provideRouter(routes,withComponentInputBinding(),withRouterConfig({
paramsInheritanceStrategy: 'always'
}))]
})
今天介紹了 Angular 路由的基本概念與使用方式,包含建立路由、導航、動態路由以及巢狀路由等。明天會繼續介紹進階的路由相關功能。